/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "los_typedef.h"
#include "ohos_bt_def.h"
#include "ble_user.h"
#include "le/att.h"
#include "le_common.h"
#include "gatt_common/le_gatt_common.h"

// 搜索类型
#define SET_SCAN_TYPE       SCAN_ACTIVE
// 搜索 周期大小
#define SET_SCAN_INTERVAL   ADV_SCAN_MS(24) // unit: 0.625ms
// 搜索 窗口大小
#define SET_SCAN_WINDOW     ADV_SCAN_MS(8)  // unit: 0.625ms, <= SET_SCAN_INTERVAL

// 连接周期
#define BASE_INTERVAL_MIN   (6) // 最小的interval
#define SET_CONN_INTERVAL   (BASE_INTERVAL_MIN*8) // (unit:1.25ms)
// 连接latency
#define SET_CONN_LATENCY    0  // (unit:conn_interval)
// 连接超时
#define SET_CONN_TIMEOUT    400 // (unit:10ms)

// 建立连接超时
#define SET_CREAT_CONN_TIMEOUT    8000 // (unit:ms)

static BtGattClientCallbacks *gatt_client_callback;
void ohos_gatt_client_start_seach_services(int id);

#define CID_MAX_SIZE     (8)
static int gcid = 0;
static struct {
    int id;
    BtUuid uuid;
} cid_list[CID_MAX_SIZE] = {0};

scan_conn_cfg_t gatt_client_scan_cfg = {
    .scan_auto_do  = 1,
    .creat_auto_do = 1,
    .scan_type     = SET_SCAN_TYPE,
    .scan_filter   = 1,
    .scan_interval = SET_SCAN_INTERVAL,
    .scan_window   = SET_SCAN_WINDOW,

    .creat_conn_interval      = SET_CONN_INTERVAL,
    .creat_conn_latency       = SET_CONN_LATENCY,
    .creat_conn_super_timeout = SET_CONN_TIMEOUT,
    .conn_update_accept       = 1,
    .creat_state_timeout_ms   = SET_CREAT_CONN_TIMEOUT,
};

// 配置多个扫描匹配设备
static const u8 remoter_name[] = "AC897N_MX(BLE)";

static client_match_cfg_t match_device_table[] = {
    {
        .create_conn_mode = BIT(CLI_CREAT_BY_NAME),
        .compare_data_len = sizeof(remoter_name) - 1, // 去结束符
        .compare_data = remoter_name,
        .filter_pdu_bitmap = 0,
    },
};


const gatt_search_cfg_t client_search_config = {
    .match_devices = &match_device_table,
    .match_devices_count = (sizeof(match_device_table) / sizeof(client_match_cfg_t)),
    .match_rssi_enable = 0,

    .search_uuid_count = 0,
    .search_uuid_group = NULL,
    .auto_enable_ccc = 1,
};

static int client_event_packet_handler(int event, u8 *packet, u16 size, u8 *ext_param);
const gatt_client_cfg_t gatt_client_cfg = {
    .event_packet_handler = client_event_packet_handler,
};

static u16 conn_handle;
static u32 recieve_test_count[SUPPORT_MAX_GATT_CLIENT];
static  u8 search_profile_complete_flag[SUPPORT_MAX_GATT_CLIENT]; /* 搜索完服务 */

static int client_event_packet_handler(int event, u8 *packet, u16 size, u8 *ext_param)
{
    int i;
    /* printf("event: %02x,size= %d\n",event,size); */
    switch (event) {
        case GATT_COMM_EVENT_GATT_DATA_REPORT: {
                att_data_report_t *report_data = (void *)packet;
                printf("data_report:hdl=%04x,pk_type=%02x,size=%d\n", report_data->conn_handle,
                    report_data->packet_type, report_data->blob_length);
                put_buf(report_data->blob, report_data->blob_length);

                i = ble_comm_dev_get_index(report_data->conn_handle, SUPPORT_MAX_GATT_CLIENT);
                if (i != INVAIL_INDEX) {
                    recieve_test_count[i] += report_data->blob_length;
                }

                switch (report_data->packet_type) {
                    case GATT_EVENT_NOTIFICATION: // notify
                        break;
                    case GATT_EVENT_INDICATION: // indicate
                        break;
                    case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT: // read
                        break;
                    case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT: // read long
                        break;
                    default:
                        break;
                }
        }
            break;

        case GATT_COMM_EVENT_CAN_SEND_NOW:
            break;

        case GATT_COMM_EVENT_SCAN_ADV_REPORT:
            putchar('V');
            break;

        case GATT_COMM_EVENT_CONNECTION_COMPLETE:
            conn_handle = little_endian_read_16(packet, 0);
            printf("connection_handle:%04x, rssi= %d\n", conn_handle, ble_vendor_get_peer_rssi(conn_handle));
            printf("peer_address_info:");
            put_buf(&ext_param[7], 7);
            break;

        case GATT_COMM_EVENT_DISCONNECT_COMPLETE:
            printf("disconnect_handle:%04x,reason= %02x\n", little_endian_read_16(packet, 0), packet[2]);

            if (conn_handle == little_endian_read_16(packet, 0)) {
                conn_handle = 0;
            }

            i = ble_comm_dev_get_index(little_endian_read_16(packet, 0), SUPPORT_MAX_GATT_CLIENT);
            if (i != INVAIL_INDEX) {
                search_profile_complete_flag[i] = 0;
            }
            break;

        case GATT_COMM_EVENT_ENCRYPTION_CHANGE:
            printf("ENCRYPTION_CHANGE:handle=%04x,state=%d,process =%d", little_endian_read_16(packet, 0), packet[2],
                packet[3]);
            if (packet[3] == LINK_ENCRYPTION_RECONNECT) {
            }
            break;

        case GATT_COMM_EVENT_CONNECTION_UPDATE_COMPLETE:
            printf("conn_param update_complete:%04x\n", little_endian_read_16(packet, 0));
            break;

        case GATT_COMM_EVENT_SCAN_DEV_MATCH: {
            printf("match_dev:addr_type= %d\n", packet[0]);
            put_buf(&packet[1], 6);
            if (packet[8] == 2) {
                printf("is TEST_BOX\n");
            }
            client_match_cfg_t *match_cfg = ext_param;
            if (match_cfg) {
                printf("match_mode: %d\n", match_cfg->create_conn_mode);
                if (match_cfg->compare_data_len) {
                    put_buf(match_cfg->compare_data, match_cfg->compare_data_len);
                }
            }
        }
        break;

        case GATT_COMM_EVENT_GATT_SEARCH_MATCH_UUID:
            opt_handle_t *opt_hdl = packet;
            printf("match:server_uuid= %04x,charactc_uuid= %04x,value_handle= %04x\n", \
                     opt_hdl->search_uuid->services_uuid16, opt_hdl->search_uuid->characteristic_uuid16,
                         opt_hdl->value_handle);
        break;

        case GATT_COMM_EVENT_MTU_EXCHANGE_COMPLETE:
            break;

        case GATT_COMM_EVENT_GATT_SEARCH_PROFILE_COMPLETE: {
                printf("GATT_SEARCH_PROFILE_COMPLETE:%04x\n", little_endian_read_16(packet, 0));
                i = ble_comm_dev_get_index(little_endian_read_16(packet, 0), SUPPORT_MAX_GATT_CLIENT);
                if (i != INVAIL_INDEX) {
                    search_profile_complete_flag[i] = 1;
                }
            }
            break;

        case GATT_COMM_EVENT_SM_PASSKEY_INPUT: {
                u32 *key = little_endian_read_32(packet, 2);
                *key = 888888;
                printf("input_key:%6u\n", *key);
            }
            break;

        default:
            break;
    }
    return 0;
}

int BleGattcRegister(BtUuid appUuid)
{
    int i;
    for (i = 0; i < CID_MAX_SIZE; i++) {
        if (!cid_list[i].uuid.uuid) {
            break;
        }
    }

    if (i >= CID_MAX_SIZE) {
        return 0;
    }
    
    cid_list[i].id = ++gcid;
    cid_list[i].uuid.uuid    = appUuid.uuid;
    cid_list[i].uuid.uuidLen = appUuid.uuidLen;
    ble_gatt_client_module_enable(1);
    return cid_list[i].id;
}

int BleGattcUnRegister(int clientId)
{
    int i;
    for (i = 0; i < CID_MAX_SIZE; i++) {
        if (cid_list[i].id == clientId) {
            break;
        }
    }
    
    if (i >= CID_MAX_SIZE) {
        return OHOS_BT_STATUS_FAIL;
    }
    
    cid_list[i].id = 0;
    cid_list[i].uuid.uuid    = NULL;
    cid_list[i].uuid.uuidLen = 0;

    ble_gatt_client_module_enable(0);
    return OHOS_BT_STATUS_SUCCESS;
}

BtGattClientCallbacks *ohos_gatt_client_callback(void)
{
    return gatt_client_callback;
}

int BleGattcConnect(int clientId, BtGattClientCallbacks *func, const BdAddr *bdAddr, bool isAutoConnect,
    BtTransportType transport)
{
    int ret;
    gatt_client_callback = func;
    ret = ble_gatt_client_create_connection_request(bdAddr->addr, 0, isAutoConnect | transport);
    return ret ? OHOS_BT_STATUS_FAIL : OHOS_BT_STATUS_SUCCESS;
}

int BleGattcDisconnect(int clientId)
{
    int ret;
    ret = ble_comm_disconnect(conn_handle);
    os_time_dly(1);
    return ret ? OHOS_BT_STATUS_FAIL : OHOS_BT_STATUS_SUCCESS;
}

int BleGattcSearchServices(int clientId)
{
	ohos_gatt_client_start_seach_services(clientId);
	return OHOS_BT_STATUS_SUCCESS;
}






